<?php

/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2019/1/18
 * Time: 16:42
 */

class Ws
{

    //监听ip
    protected $host;
    //监听端口
    protected $port;
    //服务对象
    protected $WsServer;

    public function __construct($port = 9051,$port2=9502, $host = '0.0.0.0')
    {

        $this->host = $host;
        $this->port = $port;
        // 实例化swoole Ws对象
        $this->WsServer = new \Swoole\websocket\Server($this->host, $this->port);
        //设置访问静态资源文件
        $this->WsServer->set([
            // 静态资源设置路径
            'document_root' => 'G:\haibei\websocket\swoole\tp5\public\static',
            'worker_num' => 5,//设置worker进程数量不写默认是4个
            'enable_static_handler' => true,//开启静态访问
            'task_worker_num'=>5, //开启task 任务必须实现 onTask onFinish
            'open_http_protocol'=>true,//监听的端口如果希望仍然使用Http/WebSocket协议，需要在配置中增加open_http_protocol => true 和 open_websocket_protocol => true
            'open_websocket_protocol'=>true
        ]);
        //监听多端口
        $this->WsServer->listen($host, $port2, SWOOLE_SOCK_TCP);
        // 启动进程回调函数
        $this->WsServer->on('start', [$this, 'onStart']);
        // 监听客户端链接并打开(有客服端有链接就会触发)
        $this->WsServer->on('open', [$this, 'onOpen']);
        // 监听消息事件
        $this->WsServer->on('message', [$this, 'onMessage']);
        //监听task worker requset 等事件前执行的事件
        $this->WsServer->on('WorkerStart', [$this, 'onWorkerStart']);
        // 监听task任务事件
        $this->WsServer->on('task', [$this, 'onTask']);
//        task 任务回调 onTask 执行完毕 回调 onFinish方法
        $this->WsServer->on('finish', [$this, 'onFinish']);
        //监听请求事件
        $this->WsServer->on('request', [$this, 'Ws_request']);
        // 监听关闭事件
        $this->WsServer->on('close', [$this, 'onClose']);
        // 开启服务
        $this->WsServer->start();

    }

    /**
     * @param swoole_server $server
     * 启动主进程回调本函数设置
     */
    public function onStart(swoole_server $server){
        //设置主进程别名
        swoole_set_process_name('live_master');
    }


//此事件在Worker进程/Task进程启动时发生。这里创建的对象可以在进程生命周期内使用
    public function onWorkerStart(swoole_server $server, $worker_id)
    {
//        $cid = go(function () {
//        echo "co 1 start\n";
//        co::yield();
//        echo "co 1 end\n";
//    });
//
//        go(function () use ($cid) {
//            echo "co 2 start\n";
//            co::sleep(0.5);
//            co::resume($cid);
//            echo "co 2 end\n";
//        });
//        执行requset 事件前加载tp5框架核心文件
        // 加载基础文件
        require __DIR__ . '/../thinkphp/base.php';
        think\Container::get('app')->run()->send();
    }

    //监听Ws请求事件
    public function Ws_request(Swoole_Http_Request $request, Swoole_Http_Response $response)
    {

//        $response->header('Content-Encoding','gzip');
//        $content = gzencode(11);
//        $response->gzip();

//        注意事项
//        1 进程没有关闭框架变量没有释放
//        重写swoole的server全局变量
// 把swoole接收的信息转换为thinkphp可识别的
//        $_SERVER = [];
        if (isset($request->server)) {
            foreach ($request->server as $key => $value) {
                $_SERVER[strtoupper($key)] = $value;
            }
        }
        if (isset($request->header)) {
            foreach ($request->header as $key => $value) {
                $_SERVER[strtoupper($key)] = $value;
            }
        }
        // swoole对于超全局数组：$_SERVER、$_GET、$_POST、define不会释放
        $_GET = [];
        if (isset($request->get)) {
            foreach ($request->get as $key => $value) {
                $_GET[$key] = $value;
            }
        }
        $_POST = [];
        if (isset($request->post)) {
            foreach ($request->post as $key => $value) {
                $_POST[$key] = $value;
            }
        }
        $_FILES =[];
        if (isset($request->files)) {
            foreach ($request->files as $key => $value) {
                $_FILES[$key] = $value;
            }
        }
        $this->writeLog();//记录请求日志
//        吧swooleWs对象 赋值全局变量 tp框架中都可以使用
        $_POST['swoole_http_obj']= $this->WsServer;

//        执行tp框架
        try{
            ob_start();
            think\Container::get('app')->run()->send();
            $content = ob_get_contents();
            ob_end_clean();
        }catch (Exception $e){
            print_r($e->getMessage());
            return;
        }
        // 执行应用并响应
        //返回客户端消息
        $response->end($content);
//        $this->WsServer->reload();
    }

    /**
    /**  实现本方法必须实现onFinish事件
     * @param swoole_websocket_server $server 服务对象 是Ws就是Ws对象 webscoket就是webscoket对象
     * @param $task_id int 任务id 由swoole扩展内自动生成
     * @param $src_worker_id  int 来自于哪个worker进程id
     * @param $data array 是任务的数据
     * @return string
     */
    public function onTask(swoole_websocket_server $server, $task_id, $src_worker_id, $data)
    {
        $method=$data['method'];
        $task = new \app\common\lib\task\Task();
        $result =$task->$method($data['data']);
        return  json_encode($result);
    }


    /**
     * @param swoole_websocket_server $server
     * @param swoole_http_request $request
     * @return string
     */
    public static function onOpen(swoole_websocket_server $server, swoole_http_request $request)
    {
        //        使用Chrome浏览器访问服务器，会产生额外的一次请求，/favicon.ico，可以在代码中响应404错误。
        if ($request->server['path_info'] == '/favicon.ico' || $request->server['request_uri'] == '/favicon.ico') {
            return '';
        }
//        链接成功数据存放在redis中
      \app\common\lib\common\Redis::sAdd(\app\common\lib\common\StringHandle::live(),$request->fd);

    }

    //监听客服端发送的消息  $frame 接受客户端发送的消息    // 监听消息事件
    public static function onMessage(swoole_websocket_server $server, swoole_websocket_frame $frame)
    {
//        耗时10秒
        $data = [
            'task_worker_num'=>5,
            'task'=>1,
            'fd'=>$frame->fd
        ];
        $server->push($frame->fd, '你好世界：'.date('Y-m-d H:i:s'));
    }


    /**
     * @param swoole_websocket_server $server  服务对象 是Ws就是Ws对象 webscoket就是webscoket对象
     * @param $task_id  int 任务id 由swoole扩展内自动生成
     * @param string $data 是onTask 方法返回的数据
     */
    public function onFinish(swoole_websocket_server $server,  $task_id, string $data){
        print_r($data);
    }


    //客服端关闭链接
    public static function onClose(swoole_websocket_server $server, $fd)
    {
//        关闭链接 删除链接redis的链接数据
        \app\common\lib\common\Redis::sRem(\app\common\lib\common\StringHandle::live(),$fd);
    }


    /**
     *  记录请求日志
     */
    public function writeLog(){
        $logs = json_encode(array_merge(['date'=>date('Y-m-d H:i:s')],$_GET,$_POST,$_SERVER));
//        异步io写入文件
        swoole_async_writefile(__DIR__.'/../runtime/log/'.date('Ym').'/'.date('d').'_access.log',$logs.PHP_EOL,function ($filename){

        },FILE_APPEND);
    }



}

$re = new Ws(8080,8081);
